﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using mvcForum.Core;
using mvcForum.Core.Interfaces.Services;
using mvcForum.Core.Specifications;
using mvcForum.Web.Controllers;
using ForumUserVM = mvcForum.Web.Areas.ForumAPI.ViewModels.ForumUser;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Web.Extensions;

namespace mvcForum.Web.Areas.ForumAPI.Controllers {

	public class UserController : BaseAPIController {
		private readonly IMembershipService membership;

		public UserController(IMembershipService membership) {
			this.membership = membership;
		}

		[HttpPost]
		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Create(ForumUserVM model, String mode) {
			if (ModelState.IsValid) {
				String errorMessage = String.Empty;
				if (this.membership.CreateAccount(model.Name, model.Password, model.EmailAddress, out errorMessage)) {
					this.membership.UnlockAccount(model.Name);
					return new HttpStatusCodeResult(200);
				}

				return new HttpStatusCodeResult(500, errorMessage);
			}

			return new HttpStatusCodeResult(500, ModelState.ErrorString());
		}

		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Read(Int32 id, String mode) {
			// TODO: Access!
			ForumUser user = this.GetRepository<ForumUser>().Read(id);

			IAccount account = this.membership.GetAccountByEmailAddress(user.EmailAddress);

			return this.CustomJson(
				new ForumUserVM {
					Id = user.Id,
					Name = user.Name,
					EmailAddress = user.EmailAddress,
					Deleted = user.Deleted,
					FirstVisit = user.FirstVisit,
					LastVisit = user.LastVisit,
					LastIP = user.LastIP,
					Active = user.Active,
					Locked = account.IsLockedOut
				});
		}

		[HttpPost]
		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Update(ForumUserVM model, String mode) {
			if (ModelState.IsValid) {
				// TODO: Access!!
				ForumUser user = this.GetRepository<ForumUser>().Read(model.Id);
				user.Deleted = model.Deleted;

				IAccount account = this.membership.GetAccountByName(user.Name);
				account.EmailAddress = model.EmailAddress;
				this.membership.UpdateAccount(account);

				user.Active = model.Active;

				// TODO: Hack? Maybe move an active bit to ForumUser?
				//if ((u.IsLockedOut && !model.Active) || (!u.IsLockedOut && model.Active)) {
				//    MVCBootstrap.User usr = this.GetRepository<MVCBootstrap.User>().Read((Guid)u.ProviderUserKey);
				//    usr.Locked = !model.Active;
				//}

				this.Context.SaveChanges();

				return new HttpStatusCodeResult(200);
			}

			return new HttpStatusCodeResult(500, ModelState.ErrorString());
		}

		[HttpPost]
		[Authorize(Roles = Constants.Roles.SolutionAdmin)]
		public ActionResult UpdateRoles(Int32 id, String roles, String mode) {
			ForumUser user = this.GetRepository<ForumUser>().Read(id);

			IAccount account = this.membership.GetAccount((Object)user.ProviderId);
			String[] existingRoles = this.membership.GetRolesForAccount(account.AccountName);
			if (String.IsNullOrWhiteSpace(roles) && existingRoles.Any()) {
				this.membership.RemoveAccountFromRoles(account.AccountName, existingRoles);
			}
			else if (!String.IsNullOrWhiteSpace(roles)) {
				String[] roleArray = roles.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries);
				String[] removeRoles = existingRoles.Where(r => roleArray.Contains(r) == false).ToArray();
				String[] newRoles = roleArray.Where(r => existingRoles.Contains(r) == false).ToArray();

				if (removeRoles.Any()) {
					this.membership.RemoveAccountFromRoles(account.AccountName, removeRoles);
				}
				if (newRoles.Any()) {
					this.membership.AddAccountToRoles(account.AccountName, newRoles);
				}
			}

			return new HttpStatusCodeResult(200);
		}

		[HttpPost]
		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Unlock(Int32 id, String mode) {
			// TODO: Access
			IRepository<ForumUser> userRepo = this.GetRepository<ForumUser>();
			ForumUser user = userRepo.Read(id);

			this.membership.UnlockAccount(user.Name);

			return new HttpStatusCodeResult(200);
		}

		[HttpPost]
		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Delete(Int32 id, String mode) {
			// TODO: Access
			IRepository<ForumUser> userRepo = this.GetRepository<ForumUser>();
			ForumUser user = userRepo.Read(id);

			// Mark the user as deleted!
			user.Deleted = true;

			// We'll delete the MemberShipUser, no more logging in!
			this.membership.DeleteAccount(user.Name);

			this.Context.SaveChanges();

			return new HttpStatusCodeResult(200);
		}

		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Search(String query, String mode) {
			IEnumerable<ForumUser> results = new List<ForumUser>();
			if (!String.IsNullOrWhiteSpace(query)) {
				results = this.ForumUserRepository.ReadMany(new ForumUserSpecifications.Search(query));
			}

			return this.GetUserList(results, results.Count(), 0);
		}

		private ContentResult GetUserList(IEnumerable<ForumUser> users, Int32 pageSize, Int32 skipped) {
			Int32 count = users.Count();
			return this.CustomJson(new {
				Count = count,
				PageSize = pageSize,
				Skipped = skipped,
				Users = users
					.Skip(skipped)
					.Take(pageSize)
					.Select(u => new ForumUserVM {
						Id = u.Id,
						Name = u.Name,
						EmailAddress = u.EmailAddress,
						FirstVisit = u.FirstVisit,
						Deleted = u.Deleted,
						Active = u.Active,
						ApiUrl = Url.Action("read", "user", new { mode = "json", area = "forumapi", id = u.Id })
					}).OrderBy(u => u.Name),
				Next = ((skipped + pageSize) < count ? Url.Action("list", "user", new { area = "forumapi", mode = "json", skip = (skipped + pageSize) }) : String.Empty),
				Previous = skipped > 0 ? Url.Action("list", "user", new { area = "forumapi", mode = "json", skip = (skipped - pageSize) > 0 ? skipped - pageSize : 0 }) : String.Empty
			});
		}

		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult List(Int32? skip, String mode) {
			Int32 skipCount = 0;
			Int32 returnCount = 15;
			if (skip.HasValue && skip.Value > 0) {
				skipCount = skip.Value;
			}

			return this.GetUserList(this.ForumUserRepository.ReadAll().OrderByDescending(u => u.FirstVisit), returnCount, skipCount);
		}

		public ActionResult RoleList(Int32 id, String mode) {
			ForumUser fu = this.GetRepository<ForumUser>().Read(id);
			//MembershipUser user = Membership.GetUser((Object)fu.ProviderId);
			String[] roles = this.membership.GetRolesForAccount(fu.Name);

			return this.CustomJson(new { Roles = roles });
		}
	}
}